package com.skycan.plc.adapter.protocol.modbus;

import com.ghgande.j2mod.modbus.ModbusException;
import com.ghgande.j2mod.modbus.facade.ModbusTCPMaster;
import com.ghgande.j2mod.modbus.procimg.InputRegister;
import com.ghgande.j2mod.modbus.procimg.Register;
import com.ghgande.j2mod.modbus.procimg.SimpleRegister;
import com.ghgande.j2mod.modbus.util.BitVector;
import com.skycan.plc.adapter.model.ControlCommand;
import com.skycan.plc.adapter.model.DataPoint;
import com.skycan.plc.adapter.model.DeviceInfo;
import com.skycan.plc.adapter.protocol.ProtocolAdapter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Modbus RTU over TCP协议适配器
 */
public class ModbusRtuAdapter implements ProtocolAdapter {
    private static final Logger logger = LoggerFactory.getLogger(ModbusRtuAdapter.class);

    // 缓存连接的Modbus主站
    private final Map<String, ModbusTCPMaster> connectionCache = new ConcurrentHashMap<>();

    @Override
    public boolean connect(DeviceInfo deviceInfo) {
        String deviceKey = deviceInfo.getDeviceId();

        if (connectionCache.containsKey(deviceKey)) {
            // 已存在连接，先断开
            disconnect(deviceInfo);
        }

        try {
            ModbusTCPMaster master = new ModbusTCPMaster(deviceInfo.getIpAddress(), deviceInfo.getPort());
            master.setRetries(ModbusConstants.RETRY_COUNT);
            master.setTimeout(ModbusConstants.CONNECTION_TIMEOUT);
            master.connect();
            connectionCache.put(deviceKey, master);

            logger.info("Modbus设备连接成功: {}", deviceInfo.getDeviceId());
            return true;
        } catch (Exception e) {
            logger.error("Modbus设备连接失败: {}, 错误: {}", deviceInfo.getDeviceId(), e.getMessage());
            return false;
        }
    }

    @Override
    public void disconnect(DeviceInfo deviceInfo) {
        String deviceKey = deviceInfo.getDeviceId();
        ModbusTCPMaster master = connectionCache.get(deviceKey);

        if (master != null) {
            try {
                master.disconnect();
                connectionCache.remove(deviceKey);
                logger.info("Modbus设备断开连接: {}", deviceInfo.getDeviceId());
            } catch (Exception e) {
                logger.error("Modbus设备断开连接失败: {}, 错误: {}", deviceInfo.getDeviceId(), e.getMessage());
            }
        }
    }

    @Override
    public DataPoint readDataPoint(DeviceInfo deviceInfo, String address, DataPoint.DataType type) {
        ModbusTCPMaster master = getConnection(deviceInfo);
        if (master == null) {
            logger.error("Modbus设备未连接: {}", deviceInfo.getDeviceId());
            return null;
        }

        try {
            Object value = null;

            if (type == DataPoint.DataType.DI || type == DataPoint.DataType.DO) {
                // 读取数字量
                value = readDigital(master, address);
            } else if (type == DataPoint.DataType.AI || type == DataPoint.DataType.AO) {
                // 读取模拟量
                value = readAnalog(master, address);
            }

            if (value != null) {
                return DataPoint.builder()
                        .deviceId(deviceInfo.getDeviceId())
                        .type(type)
                        .address(address)
                        .value(value)
                        .timestamp(System.currentTimeMillis())
                        .build();
            }
        } catch (Exception e) {
            logger.error("读取Modbus数据点失败: 设备={}, 地址={}, 错误={}",
                    deviceInfo.getDeviceId(), address, e.getMessage());
        }

        return null;
    }

    @Override
    public List<DataPoint> readDataPoints(DeviceInfo deviceInfo, List<String> addresses,
                                          List<DataPoint.DataType> types) {
        List<DataPoint> dataPoints = new ArrayList<>();

        for (int i = 0; i < addresses.size(); i++) {
            DataPoint dataPoint = readDataPoint(deviceInfo, addresses.get(i), types.get(i));
            if (dataPoint != null) {
                dataPoints.add(dataPoint);
            }
        }

        return dataPoints;
    }

    @Override
    public boolean writeDataPoint(DeviceInfo deviceInfo, ControlCommand command) {
        ModbusTCPMaster master = getConnection(deviceInfo);
        if (master == null) {
            logger.error("Modbus设备未连接: {}", deviceInfo.getDeviceId());
            return false;
        }

        try {
            String address = command.getAddress();
            Object value = command.getValue();

            if (command.getType() == DataPoint.DataType.DO) {
                // 写入数字量
                return writeDigital(master, address, (Boolean) value);
            } else if (command.getType() == DataPoint.DataType.AO) {
                // 写入模拟量
                if (value instanceof Integer) {
                    return writeAnalog(master, address, ((Integer) value).intValue());
                } else if (value instanceof Float) {
                    // 将浮点数转换为两个寄存器值
                    float floatValue = (Float) value;
                    int[] registers = floatToRegisters(floatValue);
                    return writeAnalogFloat(master, address, registers);
                }
            }

            logger.error("不支持的数据类型或值类型: {}, 值类型: {}", command.getType(), value.getClass().getName());
            return false;
        } catch (Exception e) {
            logger.error("写入Modbus数据点失败: 设备={}, 地址={}, 错误={}",
                    deviceInfo.getDeviceId(), command.getAddress(), e.getMessage());
            return false;
        }
    }

    @Override
    public boolean isDeviceOnline(DeviceInfo deviceInfo) {
        ModbusTCPMaster master = connectionCache.get(deviceInfo.getDeviceId());
        return master != null && master.isConnected();
    }

    /**
     * 获取Modbus连接
     */
    private ModbusTCPMaster getConnection(DeviceInfo deviceInfo) {
        ModbusTCPMaster master = connectionCache.get(deviceInfo.getDeviceId());

        if (master == null || !master.isConnected()) {
            if (connect(deviceInfo)) {
                return connectionCache.get(deviceInfo.getDeviceId());
            }
            return null;
        }

        return master;
    }

    /**
     * 读取数字量
     */
    private Boolean readDigital(ModbusTCPMaster master, String address) throws ModbusException {
        int unitId = ModbusConstants.DEFAULT_UNIT_ID;

        if (address.startsWith(ModbusConstants.COIL_PREFIX)) {
            // 读取线圈状态
            int coilAddress = parseAddress(address);
            BitVector coils = master.readCoils(unitId, coilAddress, 1);
            return coils.getBit(0);
        } else if (address.startsWith(ModbusConstants.DISCRETE_INPUT_PREFIX)) {
            // 读取离散输入状态
            int inputAddress = parseAddress(address);
            BitVector inputs = master.readInputDiscretes(unitId, inputAddress, 1);
            return inputs.getBit(0);
        }

        throw new IllegalArgumentException("不支持的地址格式: " + address);
    }

    /**
     * 读取模拟量
     */
    private Number readAnalog(ModbusTCPMaster master, String address) throws ModbusException {
        int unitId = ModbusConstants.DEFAULT_UNIT_ID;

        if (address.startsWith(ModbusConstants.HOLDING_REGISTER_PREFIX)) {
            // 读取保持寄存器
            int registerAddress = parseAddress(address);
            Register[] registers = master.readMultipleRegisters(unitId, registerAddress, 2);
            return registersToFloat(registers);
        } else if (address.startsWith(ModbusConstants.INPUT_REGISTER_PREFIX)) {
            // 读取输入寄存器
            int registerAddress = parseAddress(address);
            InputRegister[] registers = master.readInputRegisters(unitId, registerAddress, 2);
            return inputRegistersToFloat(registers);
        }

        throw new IllegalArgumentException("不支持的地址格式: " + address);
    }

    /**
     * 写入数字量
     */
    private boolean writeDigital(ModbusTCPMaster master, String address, boolean value) throws ModbusException {
        int unitId = ModbusConstants.DEFAULT_UNIT_ID;

        if (address.startsWith(ModbusConstants.COIL_PREFIX)) {
            // 写入线圈状态
            int coilAddress = parseAddress(address);
            master.writeCoil(unitId, coilAddress, value);
            return true;
        }

        throw new IllegalArgumentException("不支持的地址格式: " + address);
    }

    /**
     * 写入模拟量
     */
    private boolean writeAnalog(ModbusTCPMaster master, String address, int value) throws ModbusException {
        int unitId = ModbusConstants.DEFAULT_UNIT_ID;

        if (address.startsWith(ModbusConstants.HOLDING_REGISTER_PREFIX)) {
            // 写入保持寄存器
            int registerAddress = parseAddress(address);
            Register register = new SimpleRegister(value);
            master.writeSingleRegister(unitId, registerAddress, register);
            return true;
        }

        throw new IllegalArgumentException("不支持的地址格式: " + address);
    }

    /**
     * 写入浮点数模拟量
     */
    private boolean writeAnalogFloat(ModbusTCPMaster master, String address, int[] values) throws ModbusException {
        int unitId = ModbusConstants.DEFAULT_UNIT_ID;

        if (address.startsWith(ModbusConstants.HOLDING_REGISTER_PREFIX)) {
            // 写入保持寄存器
            int registerAddress = parseAddress(address);
            Register[] registers = new Register[values.length];
            for (int i = 0; i < values.length; i++) {
                registers[i] = new SimpleRegister(values[i]);
            }
            master.writeMultipleRegisters(unitId, registerAddress, registers);
            return true;
        }

        throw new IllegalArgumentException("不支持的地址格式: " + address);
    }

    /**
     * 解析Modbus地址
     */
    private int parseAddress(String address) {
        // 移除地址前缀，获取实际地址
        return Integer.parseInt(address.substring(1));
    }

    /**
     * 寄存器值转换为浮点数
     */
    private float registersToFloat(Register[] registers) {
        if (registers.length < 2) {
            throw new IllegalArgumentException("转换浮点数需要至少2个寄存器");
        }

        int b1 = registers[0].getValue();
        int b2 = registers[1].getValue();

        int intValue = (b1 << 16) | b2;
        return Float.intBitsToFloat(intValue);
    }

    /**
     * 输入寄存器值转换为浮点数
     */
    private float inputRegistersToFloat(InputRegister[] registers) {
        if (registers.length < 2) {
            throw new IllegalArgumentException("转换浮点数需要至少2个寄存器");
        }

        int b1 = registers[0].getValue();
        int b2 = registers[1].getValue();

        int intValue = (b1 << 16) | b2;
        return Float.intBitsToFloat(intValue);
    }

    /**
     * 浮点数转换为寄存器值
     */
    private int[] floatToRegisters(float value) {
        int intValue = Float.floatToIntBits(value);
        int b1 = (intValue >> 16) & 0xFFFF;
        int b2 = intValue & 0xFFFF;

        return new int[]{b1, b2};
    }
}